#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <stdio.h>
#include <string.h>

//继电器控制引脚定义
#define RELAY_CH1 D2
#define RELAY_CH2 D3
#define RELAY_CH3 D4
#define RELAY_CH4 D5

const char* ssid = "DESKTOP-SE45L4B 9162";
const char* password = "&55Ay292";
//const char* mqttServer = "192.168.0.121";
const char* mqttServer = "test.ranye-iot.net";
//String mqttSubData;

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void setup() {

  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  connectWifi();                                    // 连接WiFi
  mqttClient.setServer(mqttServer, 1883);           // 设置MQTT服务器和端口号
  mqttClient.setCallback(receiveCallback);          // 设置MQTT订阅回调函数
  connectMQTTServer();                              // 连接MQTT服务器

  //初始化IO管脚
  pinMode(RELAY_CH1, OUTPUT);
  pinMode(RELAY_CH2, OUTPUT);
  pinMode(RELAY_CH3, OUTPUT);
  pinMode(RELAY_CH4, OUTPUT);
  pinMode(BUILTIN_LED, OUTPUT);//板载LED

  //初始化管脚电平
  digitalWrite(RELAY_CH1, LOW);
  digitalWrite(RELAY_CH2, LOW);
  digitalWrite(RELAY_CH3, LOW);
  digitalWrite(RELAY_CH4, LOW);
  digitalWrite(BUILTIN_LED, HIGH);
}

void loop() {
  if (mqttClient.connected()) {                     // 如果开发板成功连接服务器
      mqttClient.loop();                            // 保持心跳
  } else {                                          // 如果开发板未能成功连接服务器
    connectMQTTServer();                            // 则尝试连接服务器
  }
}

/***************************订阅主题***********************/
void subscribeTopic(){
  String topicString = "SmartHome-Pub-SetRelay";
  char subTopic[topicString.length() + 1];
  strcpy(subTopic, topicString.c_str());
  
  // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
  if(mqttClient.subscribe(subTopic)){
    Serial.print("Subscrib Topic: ");
    Serial.println(subTopic);
  } else {
    Serial.print("Subscribe Fail...");
  }  
}

/*****************************连接MQTT服务器*******************/
void connectMQTTServer() {
  // 根据ESP8266的MAC地址生成客户端ID（避免与其它ESP8266的客户端ID重名）
  String clientId = "esp8266-" + WiFi.macAddress();

  // 连接MQTT服务器
  if (mqttClient.connect(clientId.c_str())) {
    Serial.println("MQTT Server Connected.");
    Serial.println("Server Address: ");
    Serial.println(mqttServer);
    Serial.println("ClientId:");
    Serial.println(clientId);
    subscribeTopic();                               // 订阅指定主题
  } else {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state());
    delay(3000);
  }
}

/*******************ESP8266连接wifi********************/
void connectWifi() {

  WiFi.begin(ssid, password);

  //等待WiFi连接,成功连接后输出成功信息
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected!");
  Serial.println("");
}

/***********************接收订阅主题的处理函数*******************************/
void receiveCallback(char* topic, byte* payload, unsigned int length) {

  StaticJsonDocument<100> doc;//初始化json池，100为大小
  String mqttRecDataString = "";
  char *mqttRecDataByte = (char *)malloc(length + 1);//申请空间
  
  Serial.print("Message Received [");
  Serial.print(topic);
  Serial.println("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    mqttRecDataString += (char)payload[i];//byte[]转string
  }
  Serial.println("");
  Serial.print("Message Length(Bytes) ");
  Serial.println(length);

  //string转char
  memset(mqttRecDataByte, 0, length);
  strcpy(mqttRecDataByte, mqttRecDataString.c_str());
  deserializeJson(doc, mqttRecDataByte);
  if(doc.isNull()) {
    Serial.println("deserializeJson(doc, mqttRecDataByte) failed");
    return;
  }
  JsonArray cmdArray = doc["cmdlist"];//取cmdlist对象
  
  digitalWrite(RELAY_CH1, cmdArray[0].as<int>() == 0 ? LOW : HIGH);
  digitalWrite(RELAY_CH2, cmdArray[1].as<int>() == 0 ? LOW : HIGH);
  digitalWrite(RELAY_CH3, cmdArray[2].as<int>() == 0 ? LOW : HIGH);
  digitalWrite(RELAY_CH4, cmdArray[3].as<int>() == 0 ? LOW : HIGH);

  
  
  digitalWrite(BUILTIN_LED, LOW);
  delay(100);
  digitalWrite(BUILTIN_LED, HIGH);

  doc.clear();
}
